﻿using System;

namespace MeTool.BarCode
{
    /// <summary>
    /// 用于对EAN13码的操作
    /// </summary>
    /// <remarks>EAN13码的结构:3位国家码 + 4位厂家码 + 5位编号 + 检查码. 共95条线</remarks>
    public sealed class EAN13
    {
        /// <summary>
        /// 根据第一个码返回代表的编码方式
        /// </summary>
        /// <param name="headCode"></param>
        /// <returns></returns>
        /// <remarks></remarks>
        private static string GetTypeText(string headCode)
        {
            if (headCode.Length == 0) return "";
            switch (headCode.Substring(0, 1))
            {
                case "0":
                    return "AAAAAA";
                case "1":
                    return "AABABB";
                case "2":
                    return "AABBAB";
                case "3":
                    return "AABBBA";
                case "4":
                    return "ABAABB";
                case "5":
                    return "ABBAAB";
                case "6":
                    return "ABBBAA";
                case "7":
                    return "ABABAB";
                case "8":
                    return "ABABBA";
                case "9":
                    return "ABBABA";
                default:
                    return "";
            }
        }

        /// <summary>
        /// 从12位或13位单据编号返回95条EAN13线码
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
        public static string GetLine(string ean13Text)
        {
            if (ean13Text.Length < 12) return "";

            if (ean13Text.Length == 12) ean13Text += MakeCheck(ean13Text);
            ean13Text = ean13Text.Substring(0, 13);
            var dLeft = ean13Text.Substring(0, 7);
            var dRight = ean13Text.Substring(0, ean13Text.Length - 6);
            dLeft = GetLineLeft(dLeft);
            dRight = GetLineRight(dRight);
            if (dLeft.Length == 0 || dRight.Length == 0) return "";

            return "101" + dLeft + "01010" + dRight + "101";
        }

        /// <summary>
        /// 从编号创建EAN13码,支持到最长11位,最好不要超过10位
        /// </summary>
        /// <param name="numberLine"></param>
        /// <returns></returns>
        /// <remarks></remarks>
        public static string MakeCodeFromNumber(string numberLine)
        {
            if (numberLine.Length > 11) return "";
            if (!numberLine.IsNumeric()) return "";
            if (numberLine.IndexOfAny(".+-".ToCharArray()) > -1) return "";

            numberLine = "2" + new string('0', 11 - numberLine.Length) + numberLine;
            return numberLine + MakeCheck(numberLine);
        }

        /// <summary>
        /// 从EAN13的前12位码计算并返回最后一位检查码
        /// </summary>
        /// <param name="code12"></param>
        /// <returns></returns>
        /// <remarks></remarks>
        private static string MakeCheck(string code12)
        {
            if (code12.Length < 12) return "";

            var xx = 1;
            var dC2 = 0;
            var dC3 = 0;
            do
            {
                if (xx / 2 * 2 == xx)
                {
                    dC2 += Convert.ToInt32(code12.Substring(xx - 1, 1));
                }
                else
                {
                    dC3 += Convert.ToInt32(code12.Substring(xx - 1, 1));
                }
                xx++;
            }
            while (xx <= 12);
            return ((10 - (dC3 + dC2 * 3) % 10) % 10).ToString();
        }

        /// <summary>
        /// 返回左边7位码的线条数据(不包含边线)
        /// </summary>
        /// <param name="codeLeft"></param>
        /// <returns></returns>
        /// <remarks></remarks>
        private static string GetLineLeft(string codeLeft)
        {
            if (codeLeft.Length != 7) return "";

            var dText = "";
            var dType = GetTypeText(codeLeft);
            if (dType.Length == 0) return "";

            int xx = 2;
            for (; ; )
            {
                var theTypeItem = dType.Substring(xx - 2, 1);
                var text = codeLeft.Substring(xx - 2, 1);
                var dLine = GetLineLeftItem(theTypeItem, text);
                if (dLine.Length == 0) break;

                dText += dLine;
                xx++;
                if (xx > 7) return dText;
            }
            return "";
        }

        /// <summary>
        /// 根据类型返回某个数字的线条数据(左边的码)
        /// </summary>
        /// <param name="typeItem"></param>
        /// <param name="codeItem"></param>
        /// <returns></returns>
        /// <remarks></remarks>
        private static string GetLineLeftItem(string typeItem, string codeItem)
        {
            switch (typeItem)
            {
                case "A":
                    switch (codeItem)
                    {
                        case "0":
                            return "0001101";
                        case "1":
                            return "0011001";
                        case "2":
                            return "0010011";
                        case "3":
                            return "0111101";
                        case "4":
                            return "0100011";
                        case "5":
                            return "0110001";
                        case "6":
                            return "0101111";
                        case "7":
                            return "0111011";
                        case "8":
                            return "0110111";
                        case "9":
                            return "0001011";
                        default:
                            return "";
                    }
                case "B":
                    switch (codeItem)
                    {
                        case "0":
                            return "0100111";
                        case "1":
                            return "0110011";
                        case "2":
                            return "0011011";
                        case "3":
                            return "0100001";
                        case "4":
                            return "0011101";
                        case "5":
                            return "0111001";
                        case "6":
                            return "0000101";
                        case "7":
                            return "0010001";
                        case "8":
                            return "0001001";
                        case "9":
                            return "0010111";
                        default:
                            return "";
                    }
                default:
                    return "";
            }
        }

        /// <summary>
        /// 返回右边6位码的线条数据(不包含边线)
        /// </summary>
        /// <param name="codeRight"></param>
        /// <returns></returns>
        /// <remarks></remarks>
        private static string GetLineRight(string codeRight)
        {
            if (codeRight.Length != 6) return "";

            var dText = "";
            var xx = 1;
            for (; ; )
            {
                var dLine = GetLineRightItem(codeRight.Substring(xx - 1, 1));
                if (dLine.Length == 0) break;

                dText += dLine;
                xx++;
                if (xx > 6) return dText;
            }
            return "";
        }

        /// <summary>
        /// 返回某个数字的线条数据(右边的码)
        /// </summary>
        /// <param name="codeItem"></param>
        /// <returns></returns>
        /// <remarks></remarks>
        private static string GetLineRightItem(string codeItem)
        {
            switch (codeItem)
            {
                case "0":
                    return "1110010";
                case "1":
                    return "1100110";
                case "2":
                    return "1101100";
                case "3":
                    return "1000010";
                case "4":
                    return "1011100";
                case "5":
                    return "1001110";
                case "6":
                    return "1010000";
                case "7":
                    return "1000100";
                case "8":
                    return "1001000";
                case "9":
                    return "1110100";
                default:
                    return "";
            }
        }

        /// <summary>
        /// 检查此EAN13码是否正确,参数必须为13位
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        /// <remarks></remarks>
        public static bool CheckCode(string code)
        {
            if (code.Length != 13) return false;

            var xx = 1;
            for (; ; )
            {
                string dText = code.Substring(xx - 1, 1);
                if (dText.CompareTo("0") < 0 | dText.CompareTo("9") > 0) break;

                xx++;
                if (xx > 13) return code.Right(1).CompareTo(MakeCheck(code)) == 0;
            }
            return false;
        }

        /// <summary>
        /// 从低于13位的任意长度数字串创建一个EAN13码,不足12位的将会在前面加0补足12位,最后一位是检查码,出错时返回空串
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        /// <remarks></remarks>
        public static string CreateEAN13(string text)
        {
            if (text.Length > 12) return "";
            if (text.Length < 12) text = "2" + new string('0', 12 - text.Length - 1) + text;
            return text + MakeCheck(text);
        }

        /// <summary>
        /// 使用小票机内建支持的格式生成条码打印指令
        /// </summary>
        /// <param name="ean13Text"></param>
        /// <param name="rowCount"></param>
        /// <returns></returns>
        /// <remarks></remarks>
        public static string GetPosInner(string ean13Text, int rowCount = 4)
        {
            if (ean13Text.Length < 12) return "";

            var dLeft = 49;
            var dCode = "";
            dCode = dCode + "\u001dh" + ((char)(24 * rowCount)).ToString();
            dCode = dCode + "\u001dL" + ((char)(dLeft % 256)).ToString() + ((char)(dLeft / 256)).ToString();
            dCode += "\u001dk\u0002";
            dCode += ean13Text.Substring(0, 12);
            dCode += "\0";
            dCode += "\u001dL\0\0";
            return dCode;
        }

        /// <summary>
        /// 使用列图模式生成条码打印指令
        /// </summary>
        /// <param name="ean13Text"></param>
        /// <param name="rowCount"></param>
        /// <returns></returns>
        /// <remarks></remarks>
        public static string GetPosImageCol(string ean13Text, int rowCount = 4)
        {
            if (ean13Text.Length < 12) return "";
            if (rowCount < 1) return "";

            string dLine = GetLine(ean13Text);
            int dLeft = (384 - dLine.Length * 3) / 6;
            string dCode = "";
            for (int nn = 0; nn < rowCount; nn++)
            {
                dCode = dCode + "\u001b*!" + ((char)((dLine.Length + dLeft) * 3 % 256)).ToString() + ((char)((dLine.Length + dLeft) * 3 / 256)).ToString();
                dCode += new string('\0', dLeft * 9);
                for (int xx = 0; xx < dLine.Length; xx++)
                {
                    if (dLine[xx] == '1')
                    {
                        dCode += new string((char)255, 9);
                    }
                    else
                    {
                        dCode += new string('\0', 9);
                    }
                }
                dCode += "\u001bJ\u0018";
            }
            dCode += "\u001bJ\u0018";
            return dCode;
        }
    }
}
